/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import ts from '../../vendor/ohos-typescript';

export function getNodeMembers(node: ts.Node): ts.Node[] | undefined {
  let members: ts.Node[] | undefined;
  if (ts.isModuleDeclaration(node)) {
    members = (node.body && ts.isModuleBlock(node.body)) ? (node.body.statements as unknown as ts.Node[]) : undefined;
  } else if (ts.isInterfaceDeclaration(node)) {
    members = node.members as unknown as ts.Node[];
  } else if (ts.isClassDeclaration(node)) {
    members = node.members as unknown as ts.Node[];
  } else if (ts.isTypeLiteralNode(node)) {
    members = node.members as unknown as ts.Node[];
  } else if (ts.isSourceFile(node)) {
    members = node.statements as unknown as ts.Node[];
  } else if (ts.isBlock(node)) {
    members = node.statements as unknown as ts.Node[];
  }
  return members;
}

export function getMemberType(node: ts.ClassElement): string {
  if (ts.isConstructorDeclaration(node)) { return 'constructors' };
  if (ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node)) { return 'accessors' };
  if (ts.isMethodDeclaration(node)) { return 'methods' };
  if (ts.isPropertyDeclaration(node)) { return 'properties' };
  if (ts.isParameterPropertyDeclaration(node, node.parent)) { return 'parameterProperties' };
  return 'methods'; // fallback
}

export function isWarningMember(member: ts.Node, childNodes: ts.Node[]): boolean {
    const hasSpecificChildInBlock = (blockNode: ts.Node, checkNodeTypes: (node: ts.Node) => boolean): boolean => {
        const blockNodeChildren = blockNode.getChildren();
        const syntaxListNodes = blockNodeChildren.filter(child => child.kind === ts.SyntaxKind.SyntaxList);

        return syntaxListNodes.some(syntaxListNode => {
            const syntaxListChildren = syntaxListNode.getChildren();
            return syntaxListChildren.some(checkNodeTypes);
        });
    };

    switch (member.kind) {
        case ts.SyntaxKind.ExpressionStatement:
            return childNodes.some(isSpecificExpression);

        case ts.SyntaxKind.IfStatement: {
            const blockNodes = childNodes.filter(child => ts.isBlock(child));
            return blockNodes.some(blockNode =>
                hasSpecificChildInBlock(blockNode, child =>
                    ts.isVariableStatement(child) || ts.isExpressionStatement(child)
                )
            );
        }

        case ts.SyntaxKind.DoStatement:
        case ts.SyntaxKind.WhileStatement:
        case ts.SyntaxKind.ForStatement:
        case ts.SyntaxKind.ForInStatement:
        case ts.SyntaxKind.ForOfStatement:
        case ts.SyntaxKind.SwitchStatement:
        case ts.SyntaxKind.TryStatement:

            return true;

        case ts.SyntaxKind.Block: {
            const blockSyntaxListNodes = childNodes.filter(child => child.kind === ts.SyntaxKind.SyntaxList);
            return blockSyntaxListNodes.some(syntaxListNode =>
                syntaxListNode.getChildren().some(child => ts.isExpressionStatement(child))
            );
        }

        case ts.SyntaxKind.VariableStatement: {
            const variableDeclarationListNodes = childNodes.filter(child => ts.isVariableDeclarationList(child));
            return variableDeclarationListNodes.some(isVariableDeclarationWithCall);
        }

        default:
            return false;
    }
}

export function isSpecificExpression(node: ts.Node): boolean {
    return (
        ts.isCallExpression(node) ||
        ts.isPropertyAccessExpression(node) ||
        ts.isBinaryExpression(node) ||
        ts.isNewExpression(node)
    );
};

export function isVariableDeclarationWithCall(variableDeclarationListNode: ts.Node): boolean {
    const varNodeChildren = variableDeclarationListNode.getChildren();
    const syntaxListNodes = varNodeChildren.filter(child => child.kind === ts.SyntaxKind.SyntaxList);

    return syntaxListNodes.some(syntaxListNode => {
        const syntaxListChildren = syntaxListNode.getChildren();
        const varNodes = syntaxListChildren.filter(child => ts.isVariableDeclaration(child));

        return varNodes.some(varNode => {
            const varNodeChildrens = varNode.getChildren();
            return varNodeChildrens.some(child => ts.isCallExpression(child));
        });
    });
};

export function getMemberName(member: ts.ClassElement): string | null {
  if (ts.isConstructorDeclaration(member)) { return 'constructor' };
  if (!member.name) { return null };
  if (ts.isIdentifier(member.name)) { return member.name.text };
  if (ts.isStringLiteral(member.name) || ts.isNumericLiteral(member.name)) { 
    return member.name.text;
  };
  return null;
}

//获取成员类型
export function getMemberKind(member: ts.ClassElement): string {
  if (ts.isMethodDeclaration(member)) { return 'method' };
  if (ts.isPropertyDeclaration(member)) { return 'property' };
  if (ts.isGetAccessorDeclaration(member)) { return 'getter' };
  if (ts.isSetAccessorDeclaration(member)) { return 'setter' };
  return 'unknown';
}

//获取成员签名
export function getMemberSignature(member: ts.ClassElement): string {
  if (ts.isMethodDeclaration(member) || ts.isGetAccessorDeclaration(member) || ts.isSetAccessorDeclaration(member)) {
    const params = member.parameters.map(p => p.getText()).join(',');
    const returnType = (member as ts.MethodDeclaration).type?.getText() ?? 'void';
    return `(${params}):${returnType}`;
  }
  return '';
}

export function hasStaticModifier(member: ts.ClassElement): boolean {
  const modifiers = ts.canHaveModifiers(member) ? ts.getModifiers(member) : undefined;
  return modifiers?.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword) ?? false;
}
